 /**********************************************************************
 * Fichier : ExamTest.c
 *
 * Résumé : Fichier source principal du pilote. Défini les fonctions
 *          usager du module.
 *
 * Auteur : Vincent Léger
 *
 * Dernière modification : Lundi 5 novembre 2012
 *
 **********************************************************************/

 /******************************************
 *********DÉCLARATION DE LIBRAIRIES*********
 ******************************************/
#include "MyDriver.h"          //déclaration des fonctions usager et variables globales
//Ces autres fichiers sont inclus par dépendance :
//#include "KernelLibz.h"         //librairies du noyau linux
//#include "DebugMacros.h"        //macros pour debogage
//#include "MyIOCTL.h"            //définition des fonctions IOCTL
//#include "DriverDefinitions.h"  //définition de constantes
//#include "DriverCore.h"         //fonctions internes et structures personnelles du pilote

 /******************************************
 **********INFORMATIONS DU MODULE***********
 ******************************************/
MODULE_AUTHOR("V. Leger");
MODULE_LICENSE("Dual BSD/GPL");


/******************************************
*********DÉFINITIONS DE FONCTIONS**********
******************************************/

 /**********************************************************************
 *  Fonction : driverInit
 *
 *  Résumé : Fonction d'initialisation et d'installation du pilote.
 *
 *  Entrées : - aucune (void)
 *
 *  Sorties : - aucune (void)
 *
 *  Valeur de retour : Int --> 0            = succès
 *                             -EBUSY       =
 *                             -ERESTARTSYS =
 *
 *
 **********************************************************************/
static int __init driverInit(void)
{
    //déclaration de variables
    int result;
    //dev_t devno;
    //struct device *Buffer_device;

    //initialisation des variables
    result = 0;
    driverMinor = 0;
    nbDevices = 1;

    DEBUG_MESSAGE("Initialisation...");

    //ALLOCATION D'UN NUMERO D'UNITE-MATERIEL
    result = alloc_chrdev_region(&MyDevice.dev, driverMinor, nbDevices, DRIVER_NAME);
    if (result < 0)
    {
        DEBUG_VALUE("ATTENTION : Impossible d'obtenir le numéro d'unite materielle", driverMajor);
        return result;
    }
    //on prend le numéro en note pour utilisations future (desinstallation)
    driverMajor = MAJOR(MyDevice.dev);

    //generation de la classe
    DriverClass = class_create(THIS_MODULE, DRIVER_NAME);
    if (IS_ERR(DriverClass))
    {
        DEBUG_MESSAGE("ATTENTION : Incapable de creer la classe du pilote.");
        //desallocation d'unite materielle
        unregister_chrdev_region(MyDevice.dev, 1);
        return -EBUSY;
    }

    //Allocation de memoire pour les tampons
    CoreBuffer.Buffer = (unsigned char*) kmalloc(DEFAULT_BUFSIZE * sizeof(unsigned char),0);
    MyDevice.ReadBuf = (unsigned char *) kmalloc(READWRITE_BUFSIZE * sizeof(unsigned char),0);
    MyDevice.WriteBuf = (unsigned char *) kmalloc(READWRITE_BUFSIZE * sizeof(unsigned char),0);
    //note: 0 = REALLY SLOW AND SAFE, GFP_KERNEL = slow and safe, GFP_ATOMIC = quick and interruptible
    //probleme ?
    if (IS_ERR(CoreBuffer.Buffer) || IS_ERR(MyDevice.ReadBuf) || IS_ERR(MyDevice.WriteBuf))
    {
        DEBUG_MESSAGE("ATTENTION : Probleme dans l'allocation de memoire pour les tampons !!!");
        //liberation de la memoire allouée
        kfree(CoreBuffer.Buffer);
        kfree(MyDevice.ReadBuf);
        kfree(MyDevice.WriteBuf);
        //destruction de la classe
        class_destroy(DriverClass);
        //desallocation d'unite materielle
        unregister_chrdev_region(MyDevice.dev, 1);
        return -EBUSY;
    }

    //Initialisation des parametres du pilote
    MyDevice.numReader = 0;
    MyDevice.numWriter = 0;

    //Initialisation de semaphores
    sema_init(&MyDevice.SemBuf, 1);
    //sem_destroy ?
    //sema_init ?
    //MyDevice.DriverAccess = SPIN_LOCK_UNLOCKED;
    spin_lock_init(&MyDevice.DriverAccess);
    //notes:
    //spin_lock
    //spin_unlock
    //spin_trylock
    //spin_destroy?
    //spin_destroy(&MyDevice.DriverAccess);
    //sema_init(&BDev.SemWrite, 1); //not used... only one writer MAX
    //sema_init(&MyDevice.SemRead, 1);

    //Initialisation des valeurs internes du tampon
    CoreBuffer.BufSize = DEFAULT_BUFSIZE;
    CoreBuffer.InIdx= 0;
    CoreBuffer.OutIdx = 0;
    CoreBuffer.BufFull = 0;
    CoreBuffer.BufEmpty = 1;

    //inscription dans /dev
    cdev_init(&MyDevice.cdev, &Buf_fops);
    MyDevice.cdev.owner = THIS_MODULE;
    MyDevice.cdev.ops = &Buf_fops;
    result = cdev_add(&MyDevice.cdev, MyDevice.dev, 1);
    if (result)
    {
        DEBUG_VALUE("ATTENTION : Incapable de faire l'inscription dans /dev \nErreurr = ", (int)result);
        //liberation de la memoire des tampons
        kfree(CoreBuffer.Buffer);
        kfree(MyDevice.ReadBuf);
        kfree(MyDevice.WriteBuf);
        MyDevice.ReadBuf = NULL;
        MyDevice.WriteBuf = NULL;
        CoreBuffer.Buffer = NULL;
        //destruction de la class
        class_destroy(DriverClass);
        //desallocation d'unite materielle
        unregister_chrdev_region(MyDevice.dev, 1);
        return -EBUSY;
    }

    //create device
    DriverDevice = device_create(DriverClass, NULL, MyDevice.dev, &MyDevice, DRIVER_NAME);
    if (IS_ERR(DriverDevice))
    {
        DEBUG_MESSAGE("ATTENTION : Incapable de créer le device");
        //liberer la memoire
        kfree(CoreBuffer.Buffer);
        kfree(MyDevice.ReadBuf);
        kfree(MyDevice.WriteBuf);
        MyDevice.ReadBuf = NULL;
        MyDevice.WriteBuf = NULL;
        CoreBuffer.Buffer = NULL;
        //effacer l'inscription dans /dev
        cdev_del(&MyDevice.cdev);
        //destruction de la classe
        class_destroy(DriverClass);
        //desallocation d'unite materielle
        unregister_chrdev_region(MyDevice.dev, 1);
        return -EBUSY;
    }

    DEBUG_MESSAGE("INITIALISATION TERMINEE...");
    return 0;
}

/**********************************************************************
*  Fonction : driverExit
*
*  Résumé : Fonction de désinstallation du pilote.
*
*  Entrées : - aucune (void)
*
*  Sorties : - aucune (void)
*
*  Valeur de retour : Aucune
*
**********************************************************************/
static void __exit driverExit(void)
{
    //desallocation de la memoire des tampons
    kfree(CoreBuffer.Buffer);
    kfree(MyDevice.ReadBuf);
    kfree(MyDevice.WriteBuf);
    MyDevice.ReadBuf = NULL;
    MyDevice.WriteBuf = NULL;
    CoreBuffer.Buffer = NULL;

    //TOUT DETRUIRE
    //obtenir le numero d'unite materielle
    MyDevice.dev = MKDEV(driverMajor, driverMinor);
    //destruction du device
    device_destroy(DriverClass, MyDevice.dev);
    //effacer l'inscription dans /dev
    cdev_del(&MyDevice.cdev);
    //destruction de la classe
    class_destroy(DriverClass);
    //desallocation d'unite materielle
    unregister_chrdev_region(MyDevice.dev, 1);
    DEBUG_MESSAGE("Pilote desinstalle");
    return;
}

int driverOpen(struct inode *inode, struct file *filp)
{
    //declaration des variables
    int returnValue;
    //initialisation des variables
    returnValue = 0;
    DEBUG_MESSAGE("OUVERTURE...");
    //mode + semaphore
    if (filp->f_flags & O_NONBLOCK)
    {
        if(down_trylock(&MyDevice.SemBuf))
        {
            return -EAGAIN;
        }
    }
    else
    {
        if (down_interruptible(&MyDevice.SemBuf))
        {
            return -ERESTARTSYS;
        }
    }

    //mode d'ouverture
    if((filp->f_flags & O_ACCMODE) == O_RDONLY) //lecture seule
    {
        if(MyDevice.numReader < MAX_READERS)
        {
            MyDevice.numReader++;
            filp->private_data = &MyDevice;
        }
        else
        {
            returnValue = -ENOTTY;
        }
    }
    else if((filp->f_flags & O_ACCMODE) == O_WRONLY) //ecriture seule
    {
        if(MyDevice.numWriter < MAX_WRITERS)
        {
            MyDevice.numWriter++;
            filp->private_data = &MyDevice;
        }
        else
        {
            returnValue = -ENOTTY;
        }
    }
    else if((filp->f_flags & O_ACCMODE) == O_RDWR) //lecture + ecriture
    {
        if((MyDevice.numWriter < MAX_WRITERS)&&(MyDevice.numReader < MAX_READERS))
        {
            MyDevice.numReader++;
            MyDevice.numWriter++;
            filp->private_data = &MyDevice;
        }
        else
        {
            returnValue = -ENOTTY;   //can't have any more writers...
        }
    }
    else
    {
        return -EBUSY;
    }
    //relache du semaphore
    up(&MyDevice.SemBuf);
    return returnValue;
}

int driverRelease(struct inode *inode, struct file *filp)
{
    //variables
    int returnValue;
    struct DriverDev *Dev;
    //initialisation
    returnValue = 0;
    //recuperation de la structure de donnees personnelle
    Dev = filp->private_data;

    DEBUG_MESSAGE("OUVERTURE...");
    //mode + semaphore
    if (filp->f_flags & O_NONBLOCK)
    {
        if(down_trylock(&MyDevice.SemBuf))
        {
            return -EAGAIN;
        }
    }
    else
    {
        if (down_interruptible(&MyDevice.SemBuf))
        {
            return -ERESTARTSYS;
        }
    }

    if((filp->f_flags & O_ACCMODE) == O_RDONLY) //lecture seule
    {
        if (Dev->numReader > 0)
        {
            Dev->numReader--;
        }
    }
    else if((filp->f_flags & O_ACCMODE) == O_WRONLY) //ecriture seule
    {
        //release writer rights
        if (Dev->numWriter > 0) //robustness check
        {
            Dev->numWriter--;
        }
    }
    else if((filp->f_flags & O_ACCMODE) == O_RDWR) //lecture + ecriture
    {
        //do both...
        if (Dev->numWriter > 0) //robustness check
        {
            Dev->numWriter--;
        }
        if (Dev->numReader > 0) //robustness check
        {
            Dev->numReader--;
        }
    }
    else
    {
        returnValue = -EBUSY;
    }
    //relacher semaphore
    up(&Dev->SemBuf);
    filp->private_data = NULL;
    return returnValue;
}

ssize_t driverRead(struct file *flip, char __user *ubuf,
                    size_t count, loff_t *f_ops)
{
    return 0;
}

ssize_t driverWrite(struct file *flip, const char __user *ubuf,
                    size_t count, loff_t *f_ops)
{
    return 0;
}

long driverIoctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    //declaration de variables
    int err;
    int retval;
    int valueFromUser;
    unsigned long valueToUser;
    struct DriverDev *Dev;

    //initialisation des variables
    valueToUser = 0;
    valueFromUser = 0;
    err = 0;
    retval = 0;
    //recuperation de la structure de donnees
    Dev = filp->private_data;

    //Verifications de securite
    //type (magic number)
    if (_IOC_TYPE(cmd) != MYDRIVER_IOCTL_MAGIC_NO)
        return -ENOTTY;
    //commande valide ?
    if (_IOC_NR(cmd) > MYDRIVER_IOCTL_MAXVALUE)
        return -ENOTTY;
    //direction (lecture ou ecriture)
    if (_IOC_DIR(cmd) & _IOC_READ)
        err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
    else if (_IOC_DIR(cmd) & _IOC_WRITE)
        err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
    if (err)
        return -EFAULT;

    //capture du semaphore
    if (filp->f_flags & O_NONBLOCK)
    {
        if(down_trylock(&Dev->SemBuf))
        {
            return -EAGAIN;
        }
    }
    else
    {
        if (down_interruptible(&Dev->SemBuf))
        {
            return -ERESTARTSYS;
        }
    }

    //Verification des droits de l'usager
    if(!capable(CAP_SYS_ADMIN))
    {
        DEBUG_MESSAGE("ATTENTION : Pas les privileges necessaires...");
        up(&Dev->SemBuf);
        return -EPERM;
    }
    //traitement de la commande
    switch(cmd)
    {
        case MYDRIVER_IOCTL_RESET:
            //put everything to default (create defines)
            //BufReInit(&CoreBuffer);
            break;
        case MYDRIVER_IOCTL_GET_QTY_DATA:

            if (CoreBuffer.BufFull)
            {
                valueToUser = (long)CoreBuffer.BufSize;
            }
            else if (CoreBuffer.BufEmpty)
            {
                valueToUser = 0;
            }
            else if (CoreBuffer.InIdx > CoreBuffer.OutIdx)
            {
                valueToUser = (long)(CoreBuffer.InIdx - CoreBuffer.OutIdx);
            }
            else
            {
                valueToUser = (long)((CoreBuffer.InIdx + CoreBuffer.BufSize) - CoreBuffer.OutIdx);
            }
            retval = __put_user(valueToUser, (unsigned long __user *)arg);   //NOT SURE

            break;
        case MYDRIVER_IOCTL_GET_QTY_READER:
                retval = __put_user(Dev->numReader, (unsigned long __user *)arg);
            break;
        case MYDRIVER_IOCTL_GET_BUFFERSIZE:
            retval = __put_user(CoreBuffer.BufSize, (unsigned long __user *)arg);
            break;
        case MYDRIVER_IOCTL_SET_BUFFERSIZE:
            if((filp->f_flags & O_ACCMODE) == O_RDONLY) //autrement on a le droit (O_WRONLY ou O_RDWR)
            {
                retval = -EACCES;   //access denied
            }
            else
            {
                retval = __get_user(valueFromUser, (unsigned long __user *)arg);
                DEBUG_VALUE("new BuffSize : ",valueFromUser);
                //BufResize(&CoreBuffer, valueFromUser);
            }
            break;
        default:
            retval = -ENOTTY;   //commande non valide (aurait du etre filtre avant...)
            break;
    }
    up(&Dev->SemBuf);

    return retval;
}

